package drds.connection_pool;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * A Runnable that is scheduled in the future to report leaks.  The ScheduledFuture is
 * cancelled if the connectionProxy is closed before the leak time expires.
 */
@Slf4j
public class ProxyLeakRunnable implements Runnable
{
   static final ProxyLeakRunnable NO_LEAK;

   static
   {
      NO_LEAK = new ProxyLeakRunnable()
      {
         @Override
         void schedule(ScheduledExecutorService executorService, long leakDetectionThreshold)
         {
         }

         @Override
         public void run()
         {
         }

         @Override
         public void cancel()
         {
         }
      };
   }

   private ScheduledFuture<?> scheduledFuture;
   private String connectionName;
   private Exception exception;
   private String threadName;
   private boolean isLeaked;

   ProxyLeakRunnable(final PoolEntry poolEntry)
   {
      this.exception = new Exception("Apparent connectionProxy leak detected");
      this.threadName = Thread.currentThread().getName();
      this.connectionName = poolEntry.connection.toString();
   }

   private ProxyLeakRunnable()
   {
   }

   void schedule(ScheduledExecutorService executorService, long leakDetectionThreshold)
   {
      scheduledFuture = executorService.schedule(this, leakDetectionThreshold, TimeUnit.MILLISECONDS);
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public void run()
   {
      isLeaked = true;

      final StackTraceElement[] stackTraceElements = exception.getStackTrace();
      final StackTraceElement[] newStackTraceElements = new StackTraceElement[stackTraceElements.length - 5];
      System.arraycopy(stackTraceElements, 5, newStackTraceElements, 0, newStackTraceElements.length);

      exception.setStackTrace(newStackTraceElements);
      log.warn("Connection leak detection triggered for {} on thread {}, stack trace follows", connectionName, threadName, exception);
   }

   public void cancel()
   {
      scheduledFuture.cancel(false);
      if (isLeaked)
      {
         log.info("Previously reported leaked connectionProxy {} on thread {} was returned to the proxy (unleaked)", connectionName, threadName);
      }
   }
}
